package com.javabi.sizeof;

import java.io.PrintStream;
import java.util.Set;

/**
 * A Class Definition.
 */
// ESCA-JAVA0076: Allow magic numbers
public abstract class ClassDefinition<I> {

	/**
	 * Returns the size in bytes of the given primitive type.
	 * @param type the type.
	 * @return the size in bytes.
	 */
	protected static final long sizeOfPrimitive(Class<?> type) {
		if (type.equals(boolean.class)) {
			return MemoryUtil.SIZE_OF_BOOLEAN;
		}
		if (type.equals(char.class)) {
			return MemoryUtil.SIZE_OF_CHAR;
		}
		if (type.equals(byte.class)) {
			return MemoryUtil.SIZE_OF_BYTE;
		}
		if (type.equals(short.class)) {
			return MemoryUtil.SIZE_OF_SHORT;
		}
		if (type.equals(int.class)) {
			return MemoryUtil.SIZE_OF_INT;
		}
		if (type.equals(long.class)) {
			return MemoryUtil.SIZE_OF_LONG;
		}
		if (type.equals(float.class)) {
			return MemoryUtil.SIZE_OF_FLOAT;
		}
		if (type.equals(double.class)) {
			return MemoryUtil.SIZE_OF_DOUBLE;
		}
		throw new IllegalArgumentException("type is not primitive: " + type);
	}

	/**
	 * Returns the size of a long array with the given length.
	 * @param length the array length
	 * @return the size of the array.
	 */
	protected static final long sizeOfLongArray(long length) {
		return 16 + (length * 8);
	}

	/**
	 * Returns the size of an int array with the given length.
	 * @param length the array length
	 * @return the size of the array.
	 */
	protected static final long sizeOfIntArray(long length) {
		if (length <= 1) {
			return 16;
		}
		length -= 1;
		return 16 + ((length / 2) * 8) + (length % 2 == 0 ? 0 : 8);
	}

	/**
	 * Returns the size of a short array with the given length.
	 * @param length the array length
	 * @return the size of the array.
	 */
	protected static final long sizeOfShortArray(long length) {
		if (length <= 2) {
			return 16;
		}
		length -= 2;
		return 16 + ((length / 4) * 8) + (length % 4 == 0 ? 0 : 8);
	}

	/**
	 * Returns the size of a byte array with the given length.
	 * @param length the array length
	 * @return the size of the array.
	 */
	protected static final long sizeOfByteArray(long length) {
		if (length <= 4) {
			return 16;
		}
		length -= 4;
		return 16 + ((length / 8) * 8) + (length % 8 == 0 ? 0 : 8);
	}

	/**
	 * Returns the size of the given instance.
	 * @param instance the instance.
	 * @return the size of the given instance.
	 */
	public abstract long sizeOf(I instance);

	/**
	 * Returns true if this has elements.
	 * @param instance the instance.
	 * @return true if this has elements.
	 */
	// ESCA-JAVA0173: override
	public boolean hasElements(I instance) {
		return false;
	}

	/**
	 * Returns an element.
	 * @param instance the instance.
	 * @param definitionMap the class definition map.
	 * @param instanceSet the instance set.
	 * @param size the size.
	 * @return the element.
	 */
	// ESCA-JAVA0160: place-holder
	// ESCA-JAVA0173: place-holder
	public long sizeOfElements(I instance, ClassDefinitionMap definitionMap, Set<Object> instanceSet, long size) {
		throw new IllegalStateException("no elements");
	}

	/**
	 * Returns the size of the given instance (including debug).
	 * @param instance the instance.
	 * @param definitionMap the class definition map.
	 * @param instanceSet the instance set.
	 * @param stream the stream to print debug to.
	 * @return the size of the given instance.
	 */
	// ESCA-JAVA0173: place-holder
	public long sizeOfDebug(I instance, ClassDefinitionMap definitionMap, Set<Object> instanceSet, PrintStream stream) throws IllegalAccessException {
		throw new IllegalStateException("not implemented for class: " + instance.getClass());
	}

}
